from pythongo.base import BaseParams, BaseState, Field
from pythongo.classdef import KLineData, TickData
from pythongo.core import KLineStyleType, MarketCenter
from pythongo.base import BaseStrategy
from pythongo.utils import KLineGenerator, Scheduler
from datetime import datetime
import psutil

# 布林线反转交易策略
#   auther @盘兮 17630013170（微信同号）
# 交易逻辑：
# 1. 当股价在布林线中轨和上轨之间，且MACD为负值时，做空。
# 2. 当股价在布林线中轨和下轨之间，且MACD为正值时，做多。
# 3. 如果MACD为0，根据前一个MACD值的正负来判断趋势。
# 4. 做空时，止损为布林线上轨，止盈为布林线下轨。
# 5. 做多时，止损为布林线下轨，止盈为布林线上轨。
# 6. 止损止盈数值为整数且为双数。
# 7. 资金使用超过50%时停止策略。
# 8. 有仓位时再出现信号不开仓。
# 9. 根据市场价格实时监控止损和止盈。

class Params(BaseParams):
    exchange: str = Field(default="SHFE", title="交易所代码")
    instrument_id: str = Field(default="IF2309", title="合约代码")
    kline_style: KLineStyleType = Field(default="M1", title="K线周期")
    boll_period: int = Field(default=20, title="布林线周期", ge=1)
    boll_std_multiplier: int = Field(default=2, title="布林线标准差倍数", ge=1)
    macd_fast_period: int = Field(default=12, title="MACD快线周期", ge=1)
    macd_slow_period: int = Field(default=26, title="MACD慢线周期", ge=1)
    macd_signal_period: int = Field(default=9, title="MACD信号周期", ge=1)
    order_volume: int = Field(default=1, title="每次开仓手数", ge=1)
    capital_threshold: float = Field(default=50.0, title="资金监控阈值（百分比）")

class State(BaseState):
    last_macd: float = Field(default=0.0, title="上一个MACD值")
    has_position: bool = Field(default=False, title="是否有仓位")
    position: int = Field(default=0, title="当前仓位")  # 添加 position 属性
    stop_loss_price: float = Field(default=0.0, title="止损价格")
    take_profit_price: float = Field(default=0.0, title="止盈价格")

class BollReversal(BaseStrategy):
    def __init__(self):
        super().__init__()
        self.params_map = Params()
        self.state_map = State()
        self.kline_generator = None
        self.scheduler = Scheduler()
        self.order_queue = []
        self.investor_id = None  # 初始化时获取投资者账号

    def on_start(self):
        super().on_start()

        # 获取投资者账号
        self.investor_id = self.get_investor(1)

        self.kline_generator = KLineGenerator(
            callback=self.on_kline,
            exchange=self.params_map.exchange,
            instrument_id=self.params_map.instrument_id,
            style=self.params_map.kline_style
        )
        self.kline_generator.push_history_data()

    def on_stop(self):
        self.scheduler.stop()

    def on_kline(self, kline: KLineData):
        # 检查资金使用是否超过阈值
        used_capital = self.get_used_capital_percentage()
        if used_capital > self.params_map.capital_threshold:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 资金使用超过阈值 {self.params_map.capital_threshold}%, 停止策略")
            self.stop()
            return

        # 计算布林线指标  Todo 
        upper_band, lower_band = self.kline_generator.producer.boll(
            timeperiod=self.params_map.boll_period,
            deviation=self.params_map.boll_std_multiplier
        )

        middle_band = (upper_band + lower_band) / 2
        # self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}]布林线参数：{middle_band,upper_band,lower_band}")

        # 计算MACD指标  Todo 
        macd, signal, hist = self.kline_generator.producer.macd(
            fast_period=self.params_map.macd_fast_period,
            slow_period=self.params_map.macd_slow_period,
            signal_period=self.params_map.macd_signal_period
        )
        # self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}]MACD参数：{macd,signal,hist}")


        if self.state_map.has_position:
            #self.output(f"[{datetime.now()}] [INFO] 当前已有仓位，跳过信号生成。")
            return

        # 更新 last_macd
        self.state_map.last_macd = hist

         # 打印当前指标值
        #self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 当前K线数据: Close={kline.close}, Upper Band={upper_band}, Middle Band={middle_band}, Lower Band={lower_band}")
        #self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 当前MACD指标: MACD={macd}, Signal={signal}, Hist={hist}")

        if kline.close > middle_band and kline.close < upper_band:
            if hist < 0 or (hist == 0 and self.state_map.last_macd < 0):
                self.order_queue.append(("卖空", kline, upper_band, lower_band))
                self.state_map.has_position = True
                self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 生成做空信号: Close={kline.close}, MACD={hist}, 上一个MACD={self.state_map.last_macd}")
        elif kline.close < middle_band and kline.close > lower_band:
            if hist > 0 or (hist == 0 and self.state_map.last_macd > 0):
                self.order_queue.append(("买多", kline, lower_band, upper_band))
                self.state_map.has_position = True
                self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 生成做多信号: Close={kline.close}, MACD={hist}, 上一个MACD={self.state_map.last_macd}")

    def flush_orders(self):
        while self.order_queue:
            signal, kline, stop_loss, take_profit = self.order_queue.pop(0)
            self._execute(signal, kline, stop_loss, take_profit)

    def _execute(self, signal, kline, stop_loss, take_profit,):
        if not self.trading:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 交易未开启，跳过订单发送。")
            return

        direction = "buy" if signal == "买多" else "sell"
        pos = self.get_position(self.params_map.instrument_id)
        net_pos = pos.net_position

        if signal == "买多" and net_pos != 0:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 已有仓位，不允许重复做多。")
            return
        if signal == "卖空" and net_pos != 0:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 已有仓位，不允许重复做空。")
            return

        # 设置止损和止盈价格
        self.state_map.stop_loss_price = round(stop_loss, 2)
        self.state_map.take_profit_price = round(take_profit, 2)

        # 使用限价报单
        price = kline.close

        # 检查订单数量是否符合交易所要求
        if self.params_map.order_volume < 1:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [ERROR] 订单数量必须至少为 1。当前数量: {self.params_map.order_volume}")
            return

        # oid = self.send_order(
        #     self.params_map.exchange,
        #     self.params_map.instrument_id,
        #     self.params_map.order_volume,
        #     price,
        #     direction
        # )
        if oid and oid != -1:
            # 更新状态参数
            self.state_map.position = self.params_map.order_volume if direction == "buy" else -self.params_map.order_volume
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 订单发送成功: 信号={signal}, 数量={self.params_map.order_volume}, 方向={direction}, 价格={price}, 止损={self.state_map.stop_loss_price}, 止盈={self.state_map.take_profit_price}")
        else:
            self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 订单发送失败，订单ID无效。")

        self.output(f"[{kline.datetime.strftime('%Y-%m-%d %H:%M:%S')}] [INFO] 当前价格: {price}, 止损价格: {self.state_map.stop_loss_price}, 止盈价格: {self.state_map.take_profit_price}")

    def on_tick(self, tick: TickData):
        super().on_tick(tick)
        if self.kline_generator:
            self.kline_generator.tick_to_kline(tick)

        # 检查是否需要止损或止盈
        if self.state_map.has_position:
            current_price = tick.last_price
            if self.state_map.position > 0:  # 多仓
                if current_price <= self.state_map.stop_loss_price:
                    self.close_position("止损")
                    self.output(f"[{datetime.now()}] [INFO] 多头触发止损，平仓。")
                elif current_price >= self.state_map.take_profit_price:
                    self.close_position("止盈")
                    self.output(f"[{datetime.now()}] [INFO] 多头触发止盈，平仓。")
            elif self.state_map.position < 0:  # 空仓
                if current_price >= self.state_map.stop_loss_price:
                    self.close_position("止损")
                    self.output(f"[{datetime.now()}] [INFO] 空头触发止损，平仓。")
                elif current_price <= self.state_map.take_profit_price:
                    self.close_position("止盈")
                    self.output(f"[{datetime.now()}] [INFO] 空头触发止盈，平仓。")

    def close_position(self, reason: str):
        pos = self.get_position(self.params_map.instrument_id)
        net_pos = pos.net_position
        direction = "sell" if net_pos > 0 else "buy"
        volume = abs(net_pos)

        oid = self.send_order(
            self.params_map.exchange,
            self.params_map.instrument_id,
            volume,
            0,  # 市价单
            direction
        )
        if oid and oid != -1:
            self.state_map.has_position = False
            self.state_map.position = 0
            self.output(f"[{datetime.now()}] [INFO] Position closed due to {reason}")

    def log_resources(self):
        cpu = psutil.cpu_percent(interval=None)
        if cpu > 70:
            self.scheduler.reschedule_job(
                "flush_orders",
                trigger="interval",
                seconds=2
            )

    def get_used_capital_percentage(self):
        # 获取当前资金使用百分比
        account_info = self.get_account_fund_data(self.investor_id)
        used_capital = account_info.margin / account_info.available * 100
        return used_capital

    def get_investor(self, index: int):
        # 获取投资者信息
        return self.get_investor_data(index).investor_id  # 确保这里不会无限递归